iT邦幫忙

2022 iThome 鐵人賽

DAY 14
0
自我挑戰組

laravel+vue 學習系列 第 14

Day14. Laravel 資料庫與 Eloquent 之二

  • 分享至 

  • xImage
  •  

一、Eloquent

  1. Eloquent 是一個 ORM (object-relational mapper), 可以用來與整個資料互動, 或是代表其中一筆資料用來操作像是新增、刪除等

  2. artisan 建立 eloquent

    # 基本建立
    php artisan make:model Product
    
    # 建立同時新增 migration 檔案
    php artisan make:model Product --migration
    
    # 建立同時新增 factory 檔案
    php artisan make:model Product --factory
    
    # 其他參數建立 --constorller, --policy 等 ... 
  1. 屬性

    • $table
        // 預設資料表會讀取轉換 Class 名稱的資料表
        // e.g. Class ProducterModel -> product_model 資料表
        // 設定 Model 的資料表名稱
        protected $table = 'product';
    
    • $PrimaryKey
        // 預設會使用 id
        // 若是其他名稱可自行設定 $primaryKey 屬性
        protected $primaryKey = 'product_id';
    
    • $incrementing
        // 當 primary key 不使用自動遞增時可設定屬性為 false
        public $incrementing = false;
    
    • $keyType
        // 當 primary key 不使用數字時, 可以設定屬性為 string
        protected $keyType = 'string';
    
    • $timestamps 與 $dateFormat
      • Laravel 預設有提供 created_at, updated_at 修改時間紀錄給開發者使用, 若開發者有自訂記錄方式, 可以設定屬性為 false
      • $dateFormat 可以設定日期格式, 內容以 PHP date() 的格式相同
        # 設定不使用 created_at, updated_at
        public $timestamps = false;
    
        # 自訂記錄名稱
        const CREATED_AT = 'created_date';
        const UPDATED_AT = 'updated_date';
    
        # 設定日期格式
        protected $dateFormat = 'U';
    
    • $connection
        // 設定資料庫連線方式
        protected $connection = 'sqlite';
    
    • 設定屬性默認值
        // 當在新建一筆資料時可以預設默認值
        protected $attributes = [
            'display' => false,
        ];
    
  2. 查詢方式

    • 基本查詢與 DB:: 靜態方法依樣可以使用查詢建構器
        $products = Product::all();
    
        $products = Product::where('category', 1)
                    ->orderBy('name')
                    ->take(10)
                    ->get();
    
    • 重整 Eloquent 物件
        # 查詢
        $product = Product::where('category', '1')->first();
    
        # 重新取得
        $fresh_product = $product->refresh();
    
        # 修改資料後重整, 取消修改內容
        $product->name = 'update name';
    
        # 恢復到修改前的內容
        $product->refresh();
    
        $product = Product::where('category', 1)->get();
    
        # e.g. reject() 類似 filters 當回傳值為 true 時會移除項目
        $product = $product->reject(function ($product){
            return !$product->display;
        });
    
  3. chunk()、chunkById() 分割查詢結果

    • 當使用像是 all() 方法時可能會有大量結果, 可以用 chunk() 來分別處理一個區域內的資料筆數, 減少記憶體的消耗
    • 第一個參數傳遞要分割的資料筆數
    • 第二個參數傳遞一個 Closure 設定要做的事情
        Product::chunk(200, function($products) {
            foreach( $producs as $product ) {
                // 要做的事情 ...
            }
        });
    
    • 另一個分割方式 chunkById(), 使用時機為需要更新資料時, 因為在 chunk() 中執行更新後, 在每次撈取時可能會重複撈到已更新資料
        # 使用 id 或其他指定來撈取資料
        # 新增地個參數指定 id 欄位
        Product::chunkById(200, function($products) {
            foreach( $producs as $product ) {
                // 要做的事情 ...
            }
        }, $column = 'id');
    
  4. lazy 與 cursors

    • 與 chunk() 效果類似, 但可以分割成單筆資料來操作, 底層還是透過 chunk() 方法實作
    • 目的也是減少記憶體消耗
        # lazy
        Product::where('category', 1)
        ->lazyById(200, $column = 'id')
        ->each->update(['category', 11]);
    
        # cursor
        $products = Product::cursor()->filter(function ($product) {
            # 返回售價超過 1000 元的商品
            return $product->price > 1000;
        });
    
  5. 設定軟刪除

    • 引入 SoftDeletes Trait
        use Illuminate\Database\Eloquent\SoftDeletes;
    
        class Product extends Model
        {
            use SoftDeletes;
        }
    
    • migration 設定 softDelete()
        # 新增軟刪除
        Schema::table('product', function(Blueprint $table){
            $table->softDeletes();
        });
    
        # 移除軟刪除
        Schema::table('product', function(Blueprint $table){
            $table->dropSoftDeletes();
        });
    
    • 使用方式
        # trashed() 進行軟刪除
        $product->trashed();
    
    
        # 恢復軟刪除
        $product->restore();
    
        # 查詢已軟刪除資料
        Product::withTrashed();
    
        # 真刪除
        $product->forceDelete();
    
  6. SoftDeletes 搭配 Pruning Models 設定定期清除軟刪除

    • model 使用 trait Prunable, 並實作 prunable() 方法
        use Illuminate\Database\Eloquent\Prunable;
    
        class Product extends Model {
            use Prunable;
    
            # 設定刪除條件, 回傳一個 \Illuminate\Database\Eloquent\Builder
            public function prunable() {
                return static::where('created_at', '<=', now()->subMonth());
            }
    
            # 設定當刪除時要執行的其他動作 e.g. 刪除商品圖片
            public function pruning() {
                // ...
            }
        }
    
    • 在 App\Console\Kernel 內修改 schedule() 方法
        protected function schedule(Schedule $schedule)
        {
            # 設定偵測 model 下有 Prunable 的 Class
            $schedule->command('model:prune')->daily();
    
            # 指定 Class, 通常是 Class 在不同資料夾
            $schedule->command('model:prune', [
                '--model' => [Product::class, Category:class],
            ])->daily();
    
            # 排除某些 Class
            $schedule->command('model:prune', [
                '--except' => [Product::class, Category:class],
            ])->daily();
        }
    
    • 批次刪除 trait MassPrunable, 比 Prunable 效率更好能夠刪除大量資料
        use Illuminate\Database\Eloquent\MassPrunable;
    
        class Product extends Model {
            use MassPrunable;
    
            # 設定刪除條件, 回傳一個 \Illuminate\Database\Eloquent\Builder
            public function prunable() {
                return static::where('created_at', '<=', now()->subMonth());
            }
        }
    
  7. Query Scopes

    • Global Scopes: 設定約束讓使用所有 model 可以使用
    • 在 App\Scopes 下建立 Class , 繼承 Illuminate\Database\Eloquent\Scope
    
        namespace App\Scopes;
    
        use Illuminate\Database\Eloquent\Builder;
        use Illuminate\Database\Eloquent\Model;
        use Illuminate\Database\Eloquent\Scope;
    
        class TestScope implements Scope {
    
            # 覆寫 apply 方法
            public function apply(Builder $builder, Model $model) {
                $builder->where('created_at', '<', now()->subYears(2000));
            }
        }
    
    • 在 Model 內覆寫 booted() 方法
        use App\Scopes\TestScope;
    
        class Product extends Model {
    
            # 也可以使用 Closure 用 static::addGlobalScope 設定
            protected static function booted() {
                static::addGlobalScope(new TestScope);
            }
        }
    
        # 查詢結果
        # select * from `product` where `created_at` < 0021-02-18 00:00:00
    
        # 當有單獨搜尋不想要使用, 可用 withoutGlobalScopes 排除條件
        Product::withoutGlobalScopes(TestScope::class)->get();
    
  8. Local Scopes

    • 與 Global 類似
    • 設定方法在 Model 內, 方法名稱須以 scope 開頭
    • 方法須帶入參數 $query 為 Builder
    • 也可以在第一個參數候傳入其他參數, 來建構查詢
        namespace App\Models;
    
        use Illuminate\Database\Eloquent\Model;
    
        class Product extends Model {
            public function scopeNew($query) {
                return $query->where('created_at', '>', date("Y-m-d H:i:s", strtotime("- 3 days")));
            }
        }
    
        # 使用 
        Product::new()->order('created_at')->get();
    
  9. 比較兩個 Model 可以使用 is() 或 isNot()方法

    $product->is($newProduct);
    $product->isNot($newProduct);
  1. 事件 Event
    • Eloquent 提供一些 hook 讓開發者可以在特定的時間點自訂要執行的動作, e.g. 創建 model 時、修改 model 時, 相關 hook 有 creating, created, updating, updtaed ... 等
    • 建立方式一, 建立 Event Class 後, 到 Models 內設定屬性 $dispatchesEvents
        namespace App\Models;
    
        use App\Events\ProductDeleted;
        use App\Events\ProductSaved;
        use Illuminate\Database\Eloquent\Model;
    
        class Product extends Model {
    
            protected $dispatchesEvents = [
                'saved' => ProductSaved::class,
                'deleted' => ProductDeleted::class
            ];
        }
    
    • 建立方式二, 在 Model 內設定 Closure
        namespace App\Models;
    
        use Illuminate\Database\Eloquent\Model;
    
        class Product extends Model {
    
            protected static function booted() {
                static::created(function ($product) {
                    // ...
                });
            }
        }
    
    • 建立方式三, Observers
        # 使用 artisan 設定 observer
        php artisan make:observer ProductObserver --model=Product
    
        # provider 內設定
        use App\Models\Product;
        use App\Observers\ProductObserver;
    
        public function boot(){
            Product::observe(ProductObserver::class);
        }
    
        # 或是可以在 provider 設定屬性 $observers
        protected $observers = [
            Product::class => [ProductObserver::class],
        ];
    
    * 設定在資料交易完成後才可以執行
    ```php
        # 設定在 observer Class 內
        public $afterCommit = true;
    ```
    
    • 取消 Events
      a. 使用 withoutEvents()
          $product = Product::withoutEvents(function () use () {
              Product::where('id', '=', '1')->delete();
      
              return Product::find(1)
          });
      
      b. 使用 {動作}Quietly() 方法
         $product->saveQuietly();
         $product->deleteQuietly();
         $product->restoreQuietly();
      

上一篇
Day13. Laravel 資料庫與 Eloquent 之一
下一篇
Day.15 網站後台建立之一 (基本頁面、js 套件)
系列文
laravel+vue 學習32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言